package com.checkbox.widgetset.client.ui;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.Style.Float;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;

import com.checkbox.widgetset.client.share.SelectableTreeNodeState;
import com.vaadin.client.VTooltip;
import com.vaadin.shared.communication.URLReference;

import java.util.LinkedList;
import java.util.List;

public class VSelectableTreeNode extends ComplexPanel implements ClickHandler {
  public static final String CLASSNAME = "v-selectabletreenode";
  public static final String CLICK_EVENT_IDENTIFIER = "click";
  public static final String CHECKBOX_CLASSNAME = "";
  public static final String EXPAND_CELL_CLASSNAME = "v-selectabletreenode-expand-cell";
  public final static String DEFAULT_EXPAND_ICON = "v-selectabletree-expend";
  public final static String DEFAULT_COLLS_ICON = "v-selectabletree-collapse";
  private final InputElement checkBox;
  private TableRowElement childBody;
  private final com.google.gwt.dom.client.Element text;
  private final TableCellElement checkBoxCell;
  private final TableCellElement mainCell;
  private TableElement childTable;
  private String childStyleName = "";
  private String nodeStyleName = "";
  private URLReference expandIcon;
  private URLReference collsIcon;
  private boolean isChildDisplay = true;
  private boolean isCollsable = true;
  private boolean isExpandIconAlignRight = false;
  private boolean isSelected = false;
  private TableCellElement textCell;
  private VSpan spanText;
  private final Element imgWrapper;
  private List<Widget> children = new LinkedList<Widget>();

  public interface SelectListener {
    void onSelectableTreeNodeSelected(VSelectableTreeNode node, boolean isSelected);
  }

  private final List<SelectListener> selectListeners = new LinkedList<SelectListener>();

  /**
   * The constructor should first call super() to initialize the component and then handle any
   * initialization relevant to Vaadin.
   */
  public VSelectableTreeNode() {
    super();
    // TODO This example code is extending the GWT Widget class so it must
    // set a root element.
    // Change to a proper element or remove this line if extending another
    // widget.
    setElement(Document.get().createTRElement());
    // As this is widget we can not trust that TR will be presented as
    // table-row without this override.
    getElement().getStyle().setProperty("display", "table-row");
    // This method call of the Paintable interface sets the component
    // style name in DOM tree
    setStyleName(CLASSNAME);
    // Tell GWT we are interested in receiving click events
    sinkEvents(Event.ONCLICK);
    // Add a handler for the click events (this is similar to
    // FocusWidget.addClickHandler())
    // addDomHandler(this, ClickEvent.getType());
    sinkEvents(VTooltip.TOOLTIP_EVENTS);
    this.checkBox = Document.get().createCheckInputElement();
    checkBox.addClassName("v-selectabletreenode-checkbox");
    this.childBody = Document.get().createTRElement();
    this.text = Document.get().createDivElement();
    this.checkBoxCell = Document.get().createTDElement();
    checkBoxCell.setVAlign("top");
    this.childBody = Document.get().createTRElement();
    this.mainCell = Document.get().createTDElement();
    this.checkBoxCell.setAttribute("width", "10px");
    this.checkBoxCell.addClassName(CHECKBOX_CLASSNAME);
    this.getElement().appendChild(checkBoxCell);
    this.getElement().appendChild(this.mainCell);
    // spanText = new VSelectableTreeNodeText();
    imgWrapper = Document.get().createDivElement();
    imgWrapper.getStyle().setFloat(Float.LEFT);
    imgWrapper.getStyle().setHeight(10, Unit.PX);
    imgWrapper.getStyle().setWidth(10, Unit.PX);
    text.appendChild(checkBox);
    text.addClassName("v-treenode-div");
  }

  public void addSelectListener(SelectListener listener) {
    selectListeners.add(listener);
  }

  public void removeSelectListener(SelectListener listener) {
    selectListeners.remove(listener);
  }

  /**
   * TODO: This is just to allow fast replacement of updateFromUIDL
   */
  public void updateFromState(SelectableTreeNodeState state) {
    boolean isCheckable = state.isCheckable;
    isCollsable = state.isCollsable;
    this.isChildDisplay = state.isExpand;
    childStyleName = state.childStyleName;
    nodeStyleName = state.nodeStyleName;
    isExpandIconAlignRight = state.isExpandIconAlignRight;
    URLReference exIcon = state.expandIcon;
    expandIcon = exIcon;
    collsIcon = state.collsIcon;
    isSelected = state.isSelect;
    this.checkBox.setChecked(isSelected);
    UIObject.setVisible(checkBox, isCheckable);
    updateCollapseImg();
    updateChildDisplay();
  }

  public boolean isChildDiplay() {
    return children.isEmpty() && isChildDisplay;
  }

  /**
   * TODO: ugly port from 6
   */
  public void reconstruct(List<Widget> children) {
    this.children = children;
    if (childTable != null) {
      childTable.removeFromParent();
    }
    this.text.removeFromParent();
    if (children.size() > 1) {
      childTable = Document.get().createTableElement();
      childTable.setAttribute("width", "100%");
      childTable.setAttribute("cellpadding", "0");
      childTable.setAttribute("cellspacing", "0");
      childTable.addClassName("v-childtie");
      this.mainCell.appendChild(childTable);
      TableSectionElement tBody = Document.get().createTBodyElement();
      childTable.appendChild(tBody);
      TableRowElement fRow = Document.get().createTRElement();
      tBody.appendChild(fRow);
      textCell = Document.get().createTDElement();
      textCell.appendChild(this.text);
      fRow.appendChild(textCell);
      fRow.addClassName(nodeStyleName);
      // imgWrapper.removeFromParent();
      // text.insertFirst(imgWrapper);
      if (this.childBody != null)
        childBody.removeFromParent();
      this.childBody = Document.get().createTRElement();
      tBody.appendChild(childBody);
      TableCellElement td = Document.get().createTDElement();
      childBody.appendChild(td);
      td.addClassName(childStyleName);
      TableElement childContent = Document.get().createTableElement();
      td.appendChild(childContent);
      tBody = Document.get().createTBodyElement();
      childContent.appendChild(tBody);
      childContent.setAttribute("width", "100%");
      childContent.setAttribute("cellpadding", "0");
      childContent.setAttribute("cellspacing", "0");
      childContent.addClassName("v-childtie");
      for (Widget child : children) {
        if (child instanceof VSpan) {
          if (spanText != null) {
            spanText.getElement().removeFromParent();
          }
          spanText = (VSpan) child;
          spanText.addStyleName("v-caption-allinline");
          add(spanText, (com.google.gwt.user.client.Element) text);
          continue;
        }
        Widget widget = child;
        add(widget, (com.google.gwt.user.client.Element) Element.as(tBody));
      }
    } else {
      if (spanText != null) {
        spanText.getElement().removeFromParent();
      }
      if (children.size() > 0) {
        spanText = (VSpan) children.get(0);
        this.mainCell.appendChild(text);
        spanText.addStyleName("v-caption-allinline");
        add(spanText, (com.google.gwt.user.client.Element) text);
      }
      imgWrapper.removeFromParent();
      textCell = this.mainCell;
    }
    processExpandIconAlign();
    com.google.gwt.user.client.Element e = spanText.getElement();
    EventListener l = new EventListener() {
      @Override
      public void onBrowserEvent(Event event) {
        onClick(null);
      }
    };
    DOM.setEventListener(e, l);
    DOM.sinkEvents(e, Event.ONCLICK);
    e = this.checkBox.cast();
    DOM.setEventListener(e, new EventListener() {
      @Override
      public void onBrowserEvent(Event event) {
        for (SelectListener listener : selectListeners) {
          listener.onSelectableTreeNodeSelected(VSelectableTreeNode.this, checkBox.isChecked());
        }
        event.stopPropagation();
      }
    });
    DOM.sinkEvents(e, Event.ONCLICK);
    updateCollapseImg();
  }

  private void processExpandIconAlign() {
    if (isExpandIconAlignRight) {
      imgWrapper.setClassName(DEFAULT_EXPAND_ICON);
      checkBoxCell.setAttribute("width", "0px");
    } else {
      imgWrapper.setClassName(DEFAULT_COLLS_ICON);
      checkBoxCell.setAttribute("width", "10px");
    }
  }

  private void updateChildDisplay() {
    if (!isCollsable) {
      UIObject.setVisible(this.childBody, true);
    } else {
      if (hasChildren() && this.childBody != null) {
        UIObject.setVisible(this.childBody, this.isChildDisplay);
      }
    }
  }

  public boolean hasChildren() {
    return !children.isEmpty();
  }

  public void updateCollapseImg() {
    if (children.size() > 1) {
      if (this.isChildDisplay) {
        imgWrapper.removeFromParent();
        imgWrapper.setClassName(DEFAULT_EXPAND_ICON);
        this.text.insertFirst(imgWrapper);
      } else {
        if (imgWrapper.getParentNode() != null)
          imgWrapper.removeFromParent();
        imgWrapper.setClassName(DEFAULT_COLLS_ICON);
        this.text.insertFirst(imgWrapper);
      }
    } else {
      if (imgWrapper.getParentNode() != null)
        text.removeChild(imgWrapper);
      // imgWrapper.getStyle().setVisibility(Visibility.HIDDEN);
    }
    for (Widget child : children) {
      if (child instanceof VSelectableTreeNode) {
        ((VSelectableTreeNode) child).updateCollapseImg();
      }
    }
  }

  /**
   * Called when a native click event is fired.
   * 
   * @param event the {@link ClickEvent} that was fired
   */
  @Override
  public void onClick(ClickEvent event) {
    if (hasChildren()) {
      this.isChildDisplay = !this.isChildDisplay;
      updateCollapseImg();
      updateChildDisplay();
    }
  }
}
